/****************************************************************************
                Университет:    Санкт-Петербургский Политехнический Университет
                Кафедра:        Компьютерные системы и программные технологии
                Аспирант:       Быков Илья Иванович
                Программа:      Расчет коэффициентов распределения
                Модуль:         node.cpp
                Назначение:
****************************************************************************/

#include <QDebug>
#include <QGraphicsScene>
#include <QGraphicsSceneMouseEvent>
#include <QPainter>
#include <QStyleOption>

#include "edge.h"
#include "node.h"

Node::Node(QColor color, qreal radius)
    : m_mode(Node::Relax)
    , m_color(color)
    , m_number(0)
    , m_highlighted(false)
    , m_voltage(0.0)
    , m_state (1)
{
    setRadius(radius);

    // Для возможности перетаскивания
    setFlag(ItemIsMovable);

    // Для возможности выделять узлы
    setFlag(ItemIsSelectable);
    setFlag(ItemSendsGeometryChanges);
    setCacheMode(DeviceCoordinateCache);
    setZValue(-1);

    m_active_power = 0.0;
    m_reactive_power = 0.0;

}

void Node::addEdge(Edge *edge)
{
    m_edgeList << edge;
    qSort(m_edgeList.begin(), m_edgeList.end(), Edge::lessThan);
    edge->adjust();
}

void Node::removeEdge(Edge *edge)
{
    m_edgeList.removeOne(edge);
}

int Node::getstate()
{
    return m_state;
}

void Node::setstate(int state)
{
    m_state = state;
}


QList<Edge*> Node::edges() const
{
    return m_edgeList;
}

QList<Edge*> Node::inEdges() const
{
    QList<Edge*> edgeList;
    foreach (Edge *edge, m_edgeList) {
        if (edge->sourceNode() != this)
            edgeList << edge;
    }
    return edgeList;
}

QList<Edge*> Node::outEdges() const
{
    QList<Edge*> edgeList;
    foreach (Edge *edge, m_edgeList) {
        if (edge->sourceNode() == this)
            edgeList << edge;
    }
    return edgeList;
}

QRectF Node::boundingRect() const
{
    QRectF rect(m_rect.left(),  m_rect.top(), m_rect.width() + 1, m_rect.height() + 1);
    return m_rect;
}

QRectF Node::boundingVoltageRect() const
{
    QRectF rect(m_rect.left() - 50,  m_rect.top() - 50, m_rect.width() + 10, m_rect.height() + 10);
    return m_rect;
}

QPainterPath Node::shape() const
{
    QPainterPath path;
    path.addEllipse(boundingRect());

    if (isSelected()) { // Квадратик для растягивания
        path.addRect(m_resizeRect);
    }

    return path;
}

void Node::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *)
{
    QColor color = m_highlighted ? QColor("#FF8000") : m_color ;
    color.setAlpha(160);

    QLinearGradient gradient(m_rect.topLeft(), m_rect.bottomRight());
    gradient.setColorAt(0, color.dark(m_highlighted ? 100 : 150));
    gradient.setColorAt(0.5, color.light(200));
    gradient.setColorAt(1, color.dark(m_highlighted ? 100 : 150));
    

    QPen pen(painter->pen());
    pen.setColor(m_highlighted ? Qt::red : Qt::black);
    pen.setWidth(isSelected() ? 2 : 1); // Толщина границы выделенного узла.
    painter->setPen(pen);
    painter->setBrush(gradient);

  /*  if (m_state == 0)
    {
        color = Qt::red;
        painter->setPen(QPen(color, 2, Qt::DashLine, Qt::RoundCap, Qt::RoundJoin));
    }
    else
        painter->setPen(QPen(color, 2, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin)); */

    painter->setRenderHint(QPainter::Antialiasing);
    painter->drawEllipse(boundingRect());
    painter->setRenderHint(QPainter::Antialiasing, false);

    pen.setWidth(1);
    pen.setColor(Qt::black);
    painter->setPen(pen);

    painter->setRenderHint(QPainter::TextAntialiasing);
    painter->drawText(boundingRect(), Qt::AlignCenter, QString::number(m_number) + "\nU="+ QString::number(m_voltage)); // Номер узла
    painter->setRenderHint(QPainter::TextAntialiasing, false);

    if (isSelected()) { // Квадратик для растягивания
        painter->setBrush(Qt::white);
        painter->drawRect(m_resizeRect);
    }

}

void Node::setColor(const QColor &color)
{
    m_color = color;
    update();
}

const QColor& Node::color() const
{
    return m_color;
}

void Node::setRadius(qreal radius)
{
    m_radius = radius;
    m_rect = QRect(-m_radius, -m_radius, m_radius * 2, m_radius * 2);
    m_resizeRect = QRect(m_rect.right() - 6, m_rect.bottom() - 6, 6, 6);
    update();
    adjustEdges();
    if (scene())
        scene()->update();
}

qreal Node::radius() const
{
    return m_radius;
}

void Node::setNumber(int number)
{
    m_number = number;
    update();
}

int Node::number() const
{
    return m_number;
}


void Node::setVoltage(double u)
{
    m_voltage = u;
}

void Node::getVoltage(double &u)
{
    u = m_voltage;
}


void Node::setPower(double actiwe_p, double reactiwe_p)
{
    m_active_power = actiwe_p;
    m_reactive_power = reactiwe_p;
}

void Node::getPower(double &actiwe_p, double &reactiwe_p)
{
    actiwe_p = m_active_power;
    reactiwe_p = m_reactive_power;
}

void Node::setMaxMinActivePower(double max_Pa, double min_Pa)
{
    m_max_Pa = max_Pa;
    m_min_Pa = min_Pa;
}

void Node::getMaxMinActivePower(double &max_Pa, double &min_Pa)
{
    max_Pa = m_max_Pa;
    min_Pa = m_min_Pa;
}


Node::Mode Node::mode() const
{
    return m_mode;
}

bool Node::linkedWith(Node *node) const
{
    foreach (Edge *edge, m_edgeList) {
        if (edge->sourceNode() == node || edge->destNode() == node)
            return true;
    }
    return false;
}

void Node::setHighlighted(bool b)
{
    m_highlighted = b;
    update();
}

bool Node::highlighted() const
{
    return m_highlighted;
}

bool Node::lessThan(Node *n1, Node *n2)
{
    return n1->number() < n2->number();
}

void Node::redraw()
{
    update();
}

QVariant Node::itemChange(GraphicsItemChange change, const QVariant &value)
{
    switch (change) {
    case ItemPositionChange:
        if (m_mode == Node::Resize) {
            QPointF point = value.toPointF(); // Точка до которой растягивать
            qreal max = qMax(point.x() - pos().x(), point.y() - pos().y()) + m_tmpRadius; // Растягивать до максимума
            if (max > 20.0) {
                setRadius(max);
                scene()->update();
            }

            return QVariant(pos()); // Позиция узла не меняется
        }
        break;
        
    case ItemSceneChange: // Удаление стрелок со сцена
        if (scene() && !value.value<QGraphicsScene*>()) {
            foreach (Edge *edge, m_edgeList) {
                if (edge->scene() == scene())
                    scene()->removeItem(edge);
            }
        }
        break;

    case ItemSceneHasChanged: // Добавление стрелок на сцену
        if (scene() && value.value<QGraphicsScene*>()) {
            foreach (Edge *edge, m_edgeList) {
                if (!edge->scene())
                    scene()->addItem(edge);
            }
        }
        break;

    default:
        break;
    };

    QVariant ret = QGraphicsItem::itemChange(change, value);

    if (change == ItemPositionHasChanged)
    {
        adjustEdges();
    }
    
    return ret;
}

void Node::adjustEdges()
{
    foreach (Edge *edge, m_edgeList) {
        edge->adjust();
    }
}

void Node::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    QGraphicsItem::mousePressEvent(event);
    QGraphicsItem *item;
    QList<QGraphicsItem*> list = scene()->selectedItems();
    m_tmpRadius = m_radius;
    if (m_resizeRect.contains(event->pos())) {
        foreach(item, list) {
            ((Node*)item)->m_mode = Node::Resize;
        }
    }
    else {
        foreach(item, list) {
            ((Node*)item)->m_mode = Node::Move;
        }
    }
    
    update();
}

void Node::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
    QList<QGraphicsItem*> list = scene()->selectedItems();
    foreach(QGraphicsItem *item, list)
    {
        ((Node*)item)->m_mode = Node::Relax;
    }
    update();
    QGraphicsItem::mouseReleaseEvent(event);
}

